package main

import (
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math/rand"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type Dot struct {
	x int
	y int
}

func (d *Dot) ToFlatIdx(width int) int {
	return d.y*width + d.x
}

func (d *Dot) FromFlatIdx(idx, width int) *Dot {
	d.x = idx % width
	d.y = idx / width
	return d
}

func getNeighbors(d *Dot) []Dot {
	pos := make([]Dot, 8)
	pos[0].x, pos[0].y = int(d.x+1), int(d.y)
	pos[1].x, pos[1].y = int(d.x+1), int(d.y-1)
	pos[2].x, pos[2].y = int(d.x), int(d.y-1)
	pos[3].x, pos[3].y = int(d.x-1), int(d.y-1)
	pos[4].x, pos[4].y = int(d.x-1), int(d.y)
	pos[5].x, pos[5].y = int(d.x-1), int(d.y+1)
	pos[6].x, pos[6].y = int(d.x), int(d.y+1)
	pos[7].x, pos[7].y = int(d.x+1), int(d.y+1)
	return pos
}

func (d *Dot) GetNE() []Dot {
	pos := make([]Dot, 3)
	pos[0].x, pos[0].y = d.x, d.y-1
	pos[1].x, pos[1].y = d.x+1, d.y-1
	pos[2].x, pos[2].y = d.x+1, d.y
	return pos
}
func (d *Dot) GetSE() []Dot {
	pos := make([]Dot, 3)
	pos[0].x, pos[0].y = d.x+1, d.y
	pos[1].x, pos[1].y = d.x+1, d.y+1
	pos[2].x, pos[2].y = d.x, d.y+1
	return pos
}
func (d *Dot) GetSW() []Dot {
	pos := make([]Dot, 3)
	pos[0].x, pos[0].y = d.x, d.y+1
	pos[1].x, pos[1].y = d.x-1, d.y+1
	pos[2].x, pos[2].y = d.x-1, d.y
	return pos
}
func (d *Dot) GetNW() []Dot {
	pos := make([]Dot, 3)
	pos[0].x, pos[0].y = d.x-1, d.y
	pos[1].x, pos[1].y = d.x-1, d.y-1
	pos[2].x, pos[2].y = d.x, d.y-1
	return pos
}

func ZoomAllDots(imgIo image.Image, walkHandler func(o image.Image, x int, y int, c color.Color)) error {

	if walkHandler == nil {
		return fmt.Errorf("walkHandler is NULL")
	}

	width := imgIo.Bounds().Dx()
	height := imgIo.Bounds().Dy()

	wg := &sync.WaitGroup{}

	for hi := 0; hi < height; hi++ {
		// use goroutine for each line
		wg.Add(1)
		go func(hi int) {
			for wi := 0; wi < width; wi++ {
				walkHandler(imgIo, wi, hi, imgIo.At(wi, hi))
			}
			wg.Done()
		}(hi)
	}
	wg.Wait()

	return nil
}

func ImgToFile(img image.Image, outputFilePath string) {
	picFile2, err := os.Create(outputFilePath)
	if err != nil {
		fmt.Printf("when create file %s error:%v\n", outputFilePath, err)
		return
	}
	defer picFile2.Close()
	if err := png.Encode(picFile2, img); err != nil {
		fmt.Println("png.Encode error:", err)
	}

}

func main() {

	inputPath := ""
	outputPath := ""

	flag.StringVar(&inputPath, "i", inputPath, "input path")
	flag.StringVar(&outputPath, "o", outputPath, "output path")
	flag.Parse()
	rand.Seed(time.Now().UnixNano())

	if outputPath == "" {
		outputDirName := filepath.Dir(inputPath)
		outputFileName := filepath.Base(inputPath)
		outputPath = outputDirName + "./out-" + outputFileName
	}

	fOld, err := os.Open(inputPath)
	if err != nil {
		fmt.Printf("open:%s failed:%v+\n", inputPath, err)
		return
	}
	defer fOld.Close()

	imgOld, err := png.Decode(fOld)
	if err != nil {
		fmt.Printf("png.Decode:%s failed:%v+\n", inputPath, err)
		return
	}

	width, height := imgOld.Bounds().Dx(), imgOld.Bounds().Dy()

	imgNew := image.NewRGBA(image.Rect(0, 0, width, height))

	dotCounter := 0
	colorSum := uint(0)

	ZoomAllDots(imgOld, func(o image.Image, x int, y int, c color.Color) {
		dotCounter++
		//只有左右。则连起来并continue
		dc := o.At(x, y)
		tr, tg, tb, _ := dc.RGBA()
		if uint(tr)+uint(tg)+uint(tb) < 0x10000*3/2 {
			imgNew.Set(x, y, color.RGBA{0, 0, 0, 0xFF})
		} else {
			imgNew.Set(x, y, color.RGBA{0xFF, 0xFF, 0xFF, 0xFF})
		}
		colorSum += uint(tr) + uint(tg) + uint(tb)
	})

	ImgToFile(imgNew, outputPath)
	fmt.Printf("dotCounter:%d colorAvg:%04x\n", dotCounter, colorSum/uint(dotCounter)/3)

}
